/*
 * Copyright 2003-2005 (c) Live Media Pty Ltd. <argot@einet.com.au> 
 *
 * This software is licensed under the Argot Public License 
 * which may be found in the file LICENSE distributed 
 * with this software.
 *
 * More information about this license can be found at
 * http://www.einet.com.au/License
 * 
 * The Developer of this software is Live Media Pty Ltd,
 * PO Box 4591, Melbourne 3001, Australia.  The license is subject 
 * to the law of Victoria, Australia, and subject to exclusive 
 * jurisdiction of the Victorian courts.
 */

#include "Types.h"

#include "common/Empty.h"
#include "common/BigEndianUnsignedByte.h"
#include "common/BigEndianUnsignedShort.h"
#include "common/U16ArrayByte.h"
#include "common/U8Ascii.h"

#include "meta/MetaExpression.h"
#include "meta/MetaAbstract.h"
#include "meta/MetaArray.h"
#include "meta/MetaBasic.h"
#include "meta/MetaDefinition.h"
#include "meta/MetaEncoding.h"
#include "meta/MetaMap.h"
#include "meta/MetaMarshaller.h"
#include "meta/MetaName.h"
#include "meta/MetaReference.h"
#include "meta/MetaSequence.h"


#include "TypeCore.h"

int type_core_init( type_library * library )
{
	int rc, u8Id, u16Id, metaBasicId, metaMapId, metaAbstractId, metaDefinitionId, metaExpressionId = -1;
	int metaSequenceId, metaArrayId, emptyId, metaNameId, metaEncodingId, metaReferenceId = -1;
	/* Register the empty type with the SystemLibrary if required.
	 * 0 - empty - a basic type representing an empty value.
	 *  - basic 0 0
	 */
	if ( type_library_get_state( library, &empty_name ) != TYPE_COMPLETE )
	{
		meta_basic * basic;

		type_library_reserve( library, &empty_name );
		type_library_reserve( library, &meta_basic_name );
	
		basic = type_malloc( sizeof( meta_basic ) );
		meta_basic_init( basic, 0, 0 );			
		emptyId = type_library_register_bind( library, &empty_name, (type_element *) basic, &empty_read, NULL, &empty_write, NULL );
		if ( emptyId <= NOTYPE )
			return emptyId;
	}
	else
	{
		emptyId = type_library_get_id( library, &empty_name );
	}

	/* Register the u8b type with the SystemLibrary if required.
	 * 1 - U8B - a basic type representing an unsigned 8bit big endian byte.
	 *   - basic 8 0
	 */
	if ( type_library_get_state( library, &uint8_name ) != TYPE_COMPLETE )
	{
		meta_basic * basic = type_malloc( sizeof( meta_basic ) );
		meta_basic_init( basic, 8, 0 );			
	
		u8Id = type_library_register_bind( library, &uint8_name, (type_element *) basic, &uint8_read, NULL, &uint8_write, NULL );
		if ( u8Id <= NOTYPE )
			return u8Id;		
	}
	else
	{
		u8Id = type_library_get_id( library, &uint8_name );
	}
	
	/* Register the u16b type with the SystemLibrary if required.
	 * 2 - U16B - a basic type of unsigned 16bit big endian integer.
	 */
	if ( type_library_get_state( library, &uint16_name ) != TYPE_COMPLETE )
	{
		meta_basic * basic = type_malloc( sizeof( meta_basic ) );
		meta_basic_init( basic, 16, 0 );			
	
		u16Id = type_library_register_bind( library, &uint16_name, (type_element *) basic, &uint16_read, NULL, &uint16_write, NULL );
		if ( u16Id <= NOTYPE )
			return u16Id;		
	}
	else
	{
		u16Id = type_library_get_id( library, &uint16_name );
	}
	
	/* Register the basic definition.
	 * 3 - BASIC - U16B
	 */
	if ( type_library_get_state( library, &meta_basic_name ) != TYPE_COMPLETE )
	{
		meta_sequence * basicSeq;
		meta_reference * basicRefSize;
		meta_reference * basicRefFlag;
		meta_expression ** basicSeqObjs;

		metaSequenceId = type_library_reserve( library, &meta_sequence_name );
		if ( metaSequenceId <= NOTYPE )
			return metaSequenceId;
			
		metaReferenceId = type_library_reserve( library, &meta_reference_name );
		if ( metaReferenceId <= NOTYPE )
			return metaReferenceId;		
	    
	    basicSeq = (meta_sequence *) type_malloc( sizeof( meta_sequence ) );
	    basicRefSize = (meta_reference *) type_malloc( sizeof( meta_reference ) );
	    basicRefFlag = (meta_reference *) type_malloc( sizeof( meta_reference ) );
	    basicSeqObjs = (meta_expression **) type_malloc( sizeof( meta_expression *) * 2 );
	    
	    meta_reference_init( basicRefSize, type_library_get_id( library, &uint8_name ), "size" );
	    meta_reference_init( basicRefFlag, type_library_get_id( library, &uint8_name ), "flags" );
	    basicSeqObjs[0] = (meta_expression *) basicRefSize;
	    basicSeqObjs[1] = (meta_expression *) basicRefFlag;
	    meta_sequence_init( basicSeq, basicSeqObjs, 2 );
	
		/* Register the Basic definition by building the model. */
		metaBasicId = type_library_register_bind( library, &meta_basic_name, (type_element *) basicSeq, &meta_basic_read, NULL, &meta_basic_write, NULL );	
		if ( metaBasicId <= NOTYPE )
			return metaBasicId;
	}
	else
	{
		metaBasicId = type_library_get_id( library, &meta_basic_name );
	}

	if ( type_library_get_state( library, &meta_name_name ) != TYPE_COMPLETE )
	{
		meta_sequence * nameSeq;
	    meta_expression ** nameSeqObjs;	
		meta_encoding * nameEnc;
		meta_array * nameAry;
		meta_reference * nameRefSize;
		meta_reference * nameRefData;

	    metaEncodingId = type_library_reserve( library, &meta_encoding_name );
   		if ( metaEncodingId <= NOTYPE )
			return metaEncodingId;
			
	    metaArrayId = type_library_reserve( library, &meta_array_name );
		if ( metaArrayId <= NOTYPE )
			return metaArrayId;	    
	    
		nameSeq = (meta_sequence *) type_malloc( sizeof(meta_sequence));
	    nameSeqObjs = (meta_expression **) type_malloc( sizeof( meta_expression *) * 1 );	
		nameEnc = (meta_encoding *) type_malloc( sizeof(meta_encoding));
		nameAry = (meta_array *) type_malloc( sizeof(meta_array));
		nameRefSize = (meta_reference *) type_malloc( sizeof(meta_reference));
		nameRefData = (meta_reference *) type_malloc( sizeof(meta_reference));
		
		meta_reference_init( nameRefData, u8Id, "data" );
		meta_reference_init( nameRefSize, u8Id, "size" );
		meta_array_init( nameAry, (meta_expression *) nameRefSize, (meta_expression *) nameRefData );
		meta_encoding_init( nameEnc, (meta_expression *) nameAry, "ISO646-US" );
	    nameSeqObjs[0] = (meta_expression *) nameEnc;		
	    meta_sequence_init( nameSeq, nameSeqObjs, 1 );
					
		metaNameId = type_library_register_bind( library, &meta_name_name, (type_element *) nameSeq, &meta_name_read, NULL, &meta_name_write, NULL );
		if ( metaNameId <= NOTYPE )
			return metaNameId;		
	}

	if ( type_library_get_state( library, &meta_abstract_name ) != TYPE_COMPLETE )
	{
	    meta_sequence * abstractSeq = (meta_sequence *) type_malloc( sizeof( meta_sequence ) );		
		meta_reference * abstractRef = (meta_reference*) type_malloc( sizeof(meta_reference));
	    meta_expression ** abstractSeqObjs = (meta_expression **) type_malloc( sizeof( meta_expression *) * 1 );
		
		meta_reference_init( abstractRef, emptyId, "abstract" );
	    abstractSeqObjs[0] = (meta_expression *) abstractRef;
	    meta_sequence_init( abstractSeq, abstractSeqObjs, 1 );
					
		metaAbstractId = type_library_register_bind( library, &meta_abstract_name, (type_element *) abstractSeq, &meta_abstract_read, NULL, &meta_abstract_write, NULL );
		if ( metaAbstractId <= NOTYPE )
			return metaAbstractId;		
	}
	else
	{
		metaAbstractId = type_library_get_id( library, &meta_abstract_name );
	}
	
	if ( type_library_get_state( library, &meta_map_name ) != TYPE_COMPLETE )
	{
		
	    meta_sequence * mapSeq = (meta_sequence *) type_malloc( sizeof( meta_sequence ) );
	    meta_reference * mapRefAbstract = (meta_reference *) type_malloc( sizeof( meta_reference ) );
	    meta_reference * mapRefConcrete = (meta_reference *) type_malloc( sizeof( meta_reference ) );
	    meta_expression ** mapSeqObjs = (meta_expression **) type_malloc( sizeof( meta_expression *) * 2 );
	    
	    meta_reference_init( mapRefAbstract, type_library_get_id( library, &uint16_name ), "abstract" );
	    meta_reference_init( mapRefConcrete, type_library_get_id( library, &uint16_name ), "concrete" );
	    mapSeqObjs[0] = (meta_expression *) mapRefAbstract;
	    mapSeqObjs[1] = (meta_expression *) mapRefConcrete;
	    meta_sequence_init( mapSeq, mapSeqObjs, 2 );
		
		metaMapId = type_library_register_bind( library, &meta_map_name, (type_element *) mapSeq, &meta_map_read, NULL, &meta_map_write, NULL );
		if ( metaMapId <= NOTYPE )
			return metaMapId;		
	}
	else
	{
		metaMapId = type_library_get_id( library, &meta_map_name );
	}
	
	/* meta.expression: abstract; */
	if ( type_library_get_state( library, &meta_expression_name ) != TYPE_COMPLETE )
	{
		meta_abstract * metaExp = (meta_abstract*) type_malloc( sizeof( meta_abstract) );
		meta_abstract_init( metaExp );
		
		metaExpressionId = type_library_register_bind( library, &meta_expression_name, (type_element *) metaExp, &meta_read, NULL, &meta_write, NULL );
		if ( metaExpressionId <= NOTYPE )
			return metaExpressionId;
	}

	if ( type_library_get_state( library, &meta_encoding_name ) != TYPE_COMPLETE )
	{
	    meta_sequence * encSeq = (meta_sequence *) type_malloc( sizeof( meta_sequence ) );
	    meta_reference * encRefData = (meta_reference *) type_malloc( sizeof( meta_reference ) );
	    meta_reference * encRefType = (meta_reference *) type_malloc( sizeof( meta_reference ) );
	    meta_expression ** encSeqObjs = (meta_expression **) type_malloc( sizeof( meta_expression *) * 2 );
	    
	    meta_reference_init( encRefData, type_library_get_id( library, &meta_expression_name ), "data" );
	    meta_reference_init( encRefType, type_library_get_id( library, &meta_name_name ), "encoding" );
	    encSeqObjs[0] = (meta_expression *) encRefData;
	    encSeqObjs[1] = (meta_expression *) encRefType;
	    meta_sequence_init( encSeq, encSeqObjs, 2 );
					
		metaEncodingId = type_library_register_bind( library, &meta_encoding_name, (type_element *) encSeq, &meta_encoding_read, NULL, &meta_encoding_write, NULL );
		if ( metaEncodingId <= NOTYPE )
			return metaEncodingId;		
	}
	else
	{
		metaEncodingId = type_library_get_id( library, &meta_encoding_name );
	}
			
	
	/* register the reference defintion
	 * 4 - U16B
	 */
	if ( type_library_get_state( library, &meta_reference_name ) != TYPE_COMPLETE )
	{
	    meta_sequence * refSeq = (meta_sequence *) type_malloc( sizeof( meta_sequence ) );
	    meta_reference * refRefType = (meta_reference *) type_malloc( sizeof( meta_reference ) );
	    meta_reference * refRefName = (meta_reference *) type_malloc( sizeof( meta_reference ) );
	    meta_expression ** refSeqObjs = (meta_expression **) type_malloc( sizeof( meta_expression *) * 2 );
	    
	    meta_reference_init( refRefType, type_library_get_id( library, &uint16_name ), "type" );
	    meta_reference_init( refRefName, type_library_get_id( library, &meta_name_name ), "name" );
	    refSeqObjs[0] = (meta_expression *) refRefType;
	    refSeqObjs[1] = (meta_expression *) refRefName;
	    meta_sequence_init( refSeq, refSeqObjs, 2 );
				
		metaReferenceId = type_library_register_bind( library, &meta_reference_name, (type_element *) refSeq, &meta_reference_read, NULL, &meta_reference_write, NULL );
		if ( metaReferenceId <= NOTYPE )
			return metaReferenceId;	
	}				
	
	/* SEQUENCE - array( identified( TypeRef | FuncRef ) ) */
	if ( type_library_get_state( library, &meta_sequence_name ) != TYPE_COMPLETE )
	{
	    meta_sequence * seqSeq = (meta_sequence *) type_malloc( sizeof( meta_sequence ) );
	    meta_array * seqAry = (meta_array *) type_malloc( sizeof( meta_array ) );
	    meta_reference * seqRefSize = (meta_reference *) type_malloc( sizeof( meta_reference ) );
	    meta_reference * seqRefType = (meta_reference *) type_malloc( sizeof( meta_reference ) );
	    meta_expression ** seqSeqObjs = (meta_expression **) type_malloc( sizeof( meta_expression *) * 2 );
	    
	    meta_reference_init( seqRefSize, type_library_get_id( library, &uint8_name ), "size" );
	    meta_reference_init( seqRefType, type_library_get_id( library, &meta_expression_name ), "type" );
	    meta_array_init( seqAry, (meta_expression *) seqRefSize, (meta_expression *) seqRefType );
	    seqSeqObjs[0] = (meta_expression *) seqAry;
	    meta_sequence_init( seqSeq, seqSeqObjs, 1 );
		
		metaSequenceId = type_library_register_bind( library, &meta_sequence_name, (type_element *) seqSeq, &meta_sequence_read, NULL, &meta_sequence_write, NULL );
		if ( metaSequenceId <= NOTYPE )
			return metaSequenceId;		
	}
	else
	{
		metaSequenceId = type_library_get_id( library, &meta_sequence_name );
	}
	
	/* register the sequence definition
	 * 8 - ARRAY - U8B <any>
	 */
	if ( type_library_get_state( library, &meta_array_name ) != TYPE_COMPLETE )
	{
	    meta_sequence * arySeq = (meta_sequence *) type_malloc( sizeof( meta_sequence ) );
	    meta_reference * aryRefSize = (meta_reference *) type_malloc( sizeof( meta_reference ) );
	    meta_reference * aryRefType = (meta_reference *) type_malloc( sizeof( meta_reference ) );
	    meta_expression ** arySeqObjs = (meta_expression **) type_malloc( sizeof( meta_expression *) * 2 );
	    
	    meta_reference_init( aryRefSize, type_library_get_id( library, &meta_expression_name ), "size" );
	    meta_reference_init( aryRefType, type_library_get_id( library, &meta_expression_name ), "type" );
	    arySeqObjs[0] = (meta_expression *) aryRefSize;
	    arySeqObjs[1] = (meta_expression *) aryRefType;
	    meta_sequence_init( arySeq, arySeqObjs, 2 );
	
		metaArrayId = type_library_register_bind( library, &meta_array_name, (type_element *) arySeq, &meta_array_read, NULL, &meta_array_write, NULL );
		if ( metaArrayId <= NOTYPE )
			return metaArrayId;
	}
	else
	{
		metaArrayId = type_library_get_id( library, &meta_array_name );
	}

	/* meta.expression#reference: map( #meta.expression, #meta.reference ); */
	if ( type_library_get_state( library, &meta_expression_reference_name ) != TYPE_COMPLETE )
	{
		meta_map * expRef = (meta_map *) type_malloc( sizeof( meta_map ));
		meta_map_init( expRef, metaExpressionId, metaReferenceId );
		rc = type_library_register_bind( library, &meta_expression_reference_name, (type_element *) expRef, &empty_read, NULL, &empty_write, NULL );
		if ( rc <= NOTYPE )
			return rc;
	}
	
	/* meta.expression#sequence: map( #meta.expression, #meta.sequence ); */
	if ( type_library_get_state( library, &meta_expression_sequence_name ) != TYPE_COMPLETE )
	{
		meta_map * expRef = (meta_map *) type_malloc( sizeof( meta_map ));
		meta_map_init( expRef, metaExpressionId, metaSequenceId );
		rc = type_library_register_bind( library, &meta_expression_sequence_name, (type_element *) expRef, &empty_read, NULL, &empty_write, NULL );
		if ( rc <= NOTYPE )
			return rc;
	}
	
	/* meta.expression#array: map( #meta.expression, #meta.array ); */
	if ( type_library_get_state( library, &meta_expression_array_name ) != TYPE_COMPLETE )
	{
		meta_map * expRef = (meta_map *) type_malloc( sizeof( meta_map ));
		meta_map_init( expRef, metaExpressionId, metaArrayId );
		rc = type_library_register_bind( library, &meta_expression_array_name, (type_element *) expRef, &empty_read, NULL, &empty_write, NULL );
		if ( rc <= NOTYPE )
			return rc;
	}
	
	/* meta.expression#array: map( #meta.expression, #meta.encoding ); */
	if ( type_library_get_state( library, &meta_expression_encoding_name ) != TYPE_COMPLETE )
	{
		meta_map * expRef = (meta_map *) type_malloc( sizeof( meta_map ));
		meta_map_init( expRef, metaExpressionId, metaEncodingId );
		rc = type_library_register_bind( library, &meta_expression_encoding_name, (type_element *) expRef, &empty_read, NULL, &empty_write, NULL );
		if ( rc <= NOTYPE )
			return rc;
	}
	
	/* meta.definition: abstract; */
	if ( type_library_get_state( library, &meta_definition_name ) != TYPE_COMPLETE )
	{
		meta_abstract * exprDef = (meta_abstract *) type_malloc( sizeof( meta_abstract ) );
		meta_abstract_init( exprDef );
		metaDefinitionId = type_library_register_bind( library, &meta_definition_name, (type_element *) exprDef, &meta_read, NULL, &meta_write, NULL );
		if ( metaDefinitionId <= NOTYPE )
			return metaDefinitionId;
	}
	else
	{
		metaDefinitionId = type_library_get_id( library, &meta_definition_name );
	}
	
	/* meta.definition#basic: map( #meta.definition, #meta.basic ); */
	if ( type_library_get_state( library, &meta_definition_basic_name ) != TYPE_COMPLETE )
	{
		meta_map * expRef = (meta_map *) type_malloc( sizeof( meta_map ));
		meta_map_init( expRef, metaDefinitionId, metaBasicId );
		rc = type_library_register_bind( library, &meta_definition_basic_name, (type_element *) expRef, &empty_read, NULL, &empty_write, NULL );
		if ( rc <= NOTYPE )
			return rc;
	}
			
	if ( type_library_get_state( library, &meta_definition_sequence_name ) != TYPE_COMPLETE )
	{
		meta_map * expRef = (meta_map *) type_malloc( sizeof( meta_map ));
		meta_map_init( expRef, metaDefinitionId, metaSequenceId );
		rc = type_library_register_bind( library, &meta_definition_sequence_name, (type_element *) expRef, &empty_read, NULL, &empty_write, NULL );
		if ( rc <= NOTYPE )
			return rc;
	}
	
	if ( type_library_get_state( library, &meta_definition_map_name ) != TYPE_COMPLETE )
	{
		meta_map * expRef = (meta_map *) type_malloc( sizeof( meta_map ));
		meta_map_init( expRef, metaDefinitionId, metaMapId );
		rc = type_library_register_bind( library, &meta_definition_map_name, (type_element *) expRef, &empty_read, NULL, &empty_write, NULL );
		if ( rc <= NOTYPE )
			return rc;
	}
	
	if ( type_library_get_state( library, &meta_definition_abstract_name ) != TYPE_COMPLETE )
	{
		meta_map * expRef = (meta_map *) type_malloc( sizeof( meta_map ));
		meta_map_init( expRef, metaDefinitionId, metaAbstractId );
		rc = type_library_register_bind( library, &meta_definition_abstract_name, (type_element *) expRef, &empty_read, NULL, &empty_write, NULL );
		if ( rc <= NOTYPE )
			return rc;
	}
	
	if ( type_library_get_state( library, &meta_definition_reference_name ) != TYPE_COMPLETE )
	{
		meta_map * expRef = (meta_map *) type_malloc( sizeof( meta_map ));
		meta_map_init( expRef, metaDefinitionId, metaReferenceId );
		rc = type_library_register_bind( library, &meta_definition_reference_name, (type_element *) expRef, &empty_read, NULL, &empty_write, NULL );
		if ( rc <= NOTYPE )
			return rc;
	}

	return TYPE_OK;
}
